home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 1993, 1994 Marc Parmet.
- * This file is part of the Macintosh port of GNU Emacs.
- *
- * GNU Emacs is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
- #if defined(THINK_C)
- #include <MacHeaders>
- #else
- #include <Types.h>
- #include <Memory.h>
- #include <Quickdraw.h>
- #include <Windows.h>
- #include <TextEdit.h>
- #include <Resources.h>
- #include <LowMem.h>
- #endif
-
- #include "errno.h"
- #include "unix-types.h"
-
- int current_process_index = 0;
- struct proc proctable[MAXPROC];
- static int next_pid = 2;
-
- #if defined(powerc)
-
- static UniversalProcPtr bridge_68k_upp;
- static UniversalProcPtr longjmp_68k_upp;
-
- enum {
- bridge_68k_ProcInfo = kCStackBased
- | RESULT_SIZE(SIZE_CODE(sizeof(int)))
- | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(int (*)())))
- | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(int)))
- | STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(char **))),
- longjmp_68k_ProcInfo = kCStackBased
- | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(long)))
- | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(long)))
- };
-
- #endif
-
- #if 0
- void
- printnum(char *s,int x)
- {
- char u[256];
- sprintf(u,"%s: %x",s,x);
- CtoPstr(u);
- DebugStr68k((unsigned char *)u);
- }
- #endif
-
- static void
- setup_stdio(int index)
- {
- int i;
- struct proc *proc = &proctable[index];
-
- /* No sharing of stdio buffers */
- for (i = 0; i<_NFILE; ++i) {
- proc->iob[i].base = 0L;
- proc->iob[i].cnt = proctable[index].iob[i].buflen = 0;
- }
-
- proc->iob[0].fd = 0;
- proc->iob[0].flag = _READ | _AUTO;
- proc->iob[1].fd = 1;
- proc->iob[1].flag = _WRITE | _AUTO;
- proc->iob[2].fd = 2;
- proc->iob[2].flag = _WRITE;
- }
-
- static void
- initialize_one_proc(struct proc *p,int ppid,char *cwd_string,char ***environ_address,
- int *errno_address)
- {
- int i;
-
- p->pid = next_pid++;
- p->ppid = ppid;
- p->vforked = 0;
- p->zombie = 0;
- p->homeResFile = 0;
- p->heapHandle = 0L;
- p->stackbottom = 0L;
- p->start_time = 0;
- p->alarm_time = 0;
- p->errno_address = errno_address;
- p->environ_address = environ_address;
- p->cwd_string = cwd_string;
- for (i = 0; i<MAXDESC; ++i)
- p->fd[i].flavor = fd_flavor_unused;
- for (i = 0; i<=NSIG; ++i)
- p->sig[i] = SIG_DFL;
- }
-
- void
- init_proc()
- {
- #if defined(powerc)
- Handle longjmp_68k,bridge_68k;
-
- bridge_68k = GetResource('CODE',129);
- if (bridge_68k == 0L) ExitToShell();
- MoveHHi(bridge_68k);
- HLock(bridge_68k);
- bridge_68k_upp = NewRoutineDescriptor((long (*)())*bridge_68k,
- bridge_68k_ProcInfo,kM68kISA);
-
- longjmp_68k = GetResource('CODE',130);
- if (longjmp_68k == 0L) ExitToShell();
- MoveHHi(longjmp_68k);
- HLock(longjmp_68k);
- longjmp_68k_upp = NewRoutineDescriptor((long (*)())*longjmp_68k,
- longjmp_68k_ProcInfo,kM68kISA);
- #endif
-
- initialize_one_proc(&proctable[current_process_index],1,0L,0L,0L);
- setup_stdio(0);
- }
-
- static int
- get_free_proc(void)
- {
- int i;
-
- for (i = 0; i<MAXPROC; ++i)
- if (proctable[i].pid == 0)
- return i;
-
- return -1;
- }
-
- static void
- restart(jmp_buf jbuf,int retcode,int from_vfork)
- {
- #if defined(powerc)
- if (current_process_index > 0 && from_vfork) {
- //printnum("Jumping back to vfork",current_process_index);
- CallUniversalProc(longjmp_68k_upp,longjmp_68k_ProcInfo,jbuf,retcode);
- }
- else
- longjmp(jbuf,retcode);
- #else
- longjmp(jbuf,retcode);
- #endif
- }
-
- static void
- start_some_process(void)
- {
- int i;
- struct proc *p;
-
- for (i = 0; i<MAXPROC; ++i) {
- current_process_index = (current_process_index + 1) % MAXPROC;
- p = &proctable[current_process_index];
- if (p->pid != 0 && !p->zombie && !p->vforked) {
- p->start_time = TickCount();
- if (p->savearea.regs.from_vfork)
- /* We are exiting vfork for the second time, back to the parent process. */
- kernel_exit(&p->savearea.lomem);
- else
- /* The last process could have been running with some other zone. */
- SetZone(ApplicZone());
- restart(p->savearea.regs.jbuf,p->savearea.regs.d0_or_r3,p->savearea.regs.from_vfork);
- }
- }
-
- /* There is no process to run. This is the usual exit point. */
- ExitToShell();
- }
-
- void
- run_another_process(void)
- {
- proctable[current_process_index].savearea.regs.from_vfork = 0;
- if (setjmp(proctable[current_process_index].savearea.regs.jbuf) == 0) {
- proctable[current_process_index].savearea.regs.d0_or_r3 = 1;
- start_some_process();
- }
- }
-
- static void
- call_signal_function(int (*f)())
- {
- #if defined(powerc)
- if (current_process_index > 0) {
- /* We are calling the handler of a utility */
- //DebugStr68k("\pCalling sigfunc of utility");
- CallUniversalProc((UniversalProcPtr)f,0);
- }
- else
- /* We are calling a handler of Emacs */
- (*f)();
- #else
- (*f)();
- #endif
- }
-
- static void
- time_for_signal_function(struct savearea_regs *regs)
- {
- call_signal_function(regs->sigfunc);
- restart(regs->jbuf,regs->d0_or_r3,regs->from_vfork);
- }
-
- static void
- queue_up_signal_function(int target_index,int (*f)(),struct savearea_lomem *lomem)
- {
- struct proc *p;
- long stacktop,regs_addr;
-
- if (target_index == current_process_index) {
- kernel_exit(lomem);
- call_signal_function(f);
- kernel_entry(lomem);
- }
- else {
- proctable[target_index].savearea.regs.sigfunc = f;
- stacktop = (long)proctable[target_index].savearea.regs.jbuf[jmp_buf_sp];
- *--*(struct savearea_regs **)&stacktop = proctable[target_index].savearea.regs;
- regs_addr = stacktop;
- *--*(long **)&stacktop = regs_addr; /* Parameter area */
- proctable[target_index].savearea.regs.from_vfork = 0;
- #if defined(powerc)
- stacktop -= sizeof(struct linkage_area); /* Fake linkage area */
- proctable[target_index].savearea.regs.jbuf[jmp_buf_sp] = (long *)stacktop;
- proctable[target_index].savearea.regs.jbuf[jmp_buf_pc] = ((long **)time_for_signal_function)[0];
- proctable[target_index].savearea.regs.jbuf[jmp_buf_toc] = ((long **)time_for_signal_function)[1];
- proctable[target_index].savearea.regs.d0_or_r3 = regs_addr;
- #else
- stacktop -= sizeof(long); /* Fake return address */
- proctable[target_index].savearea.regs.jbuf[jmp_buf_sp] = stacktop;
- proctable[target_index].savearea.regs.jbuf[jmp_buf_pc] = (long)time_for_signal_function;
- #endif
- }
- }
-
- int
- pid_to_index(int pid)
- {
- int i;
-
- for (i = 0; i<MAXPROC; ++i)
- if (proctable[i].pid == pid)
- return i;
-
- return -1;
- }
-
- static void
- remove_zombie(int index)
- {
- struct proc *p = &proctable[index];
- if (p->stackbottom) DisposPtr(p->stackbottom);
- p->pid = 0;
- }
-
- static void
- become_zombie(int index,int status)
- {
- int i,parent_index,duplicate;
- struct proc *p = &proctable[index];
- int kill_internal(int pid,int signal,struct savearea_lomem *lomem);
-
- /* Remove any child zombies */
- for (i = 0; i<MAXPROC; ++i)
- if (proctable[i].pid != 0 && proctable[i].ppid == p->pid && proctable[i].zombie)
- remove_zombie(i);
-
- p->status = status;
-
- if (p->homeResFile != 0) {
- duplicate = 0;
- for (i = 0; i<MAXPROC; ++i)
- if (i != index && proctable[i].pid != 0 &&
- proctable[i].homeResFile == p->homeResFile)
- duplicate = 1;
-
- if (!duplicate) CloseResFile(p->homeResFile);
- }
-
- if (p->heapHandle != 0L) DisposHandle(p->heapHandle);
- if (p->cwd_string != 0L) DisposPtr(p->cwd_string);
-
- parent_index = pid_to_index(p->ppid);
- if (parent_index == -1)
- p->pid = 0;
- else {
- if (proctable[parent_index].vforked == p->pid)
- proctable[parent_index].vforked = 0;
- p->zombie = 1;
- //printnum("Sending SIGCHLD to:",parent_index);
- kill_internal(proctable[parent_index].pid,SIGCHLD,(struct savearea_lomem *)0L);
- }
- }
-
- int
- kill_internal(int pid,int signal,struct savearea_lomem *lomem)
- {
- int (*f)();
- int i,target_index,retcode;
-
- if (pid < 0) {
- /* Send signal to children also. */
- for (i = 0; i<MAXPROC; ++i)
- if (proctable[i].ppid == -pid)
- kill_internal(-proctable[i].pid,signal,lomem);
- kill_internal(-pid,signal,lomem);
- }
- else if (pid == 1) {
- /* Send signal to every process. Send it to current process last. */
- for (i = 0; i<MAXPROC; ++i)
- if (proctable[i].pid != 0 && i != current_process_index)
- kill_internal(proctable[i].pid,signal,lomem);
- kill_internal(proctable[current_process_index].pid,signal,lomem);
- }
- else {
- target_index = pid_to_index(pid);
- if (target_index == -1) { set_errno(EUNDOC); return -1; }
-
- if (proctable[target_index].zombie) { set_errno(EUNDOC); return -1; }
- if (signal == 0) return 0;
- if (signal < 0 || signal > NSIG) { set_errno(EUNDOC); return -1; }
-
- f = (signal == SIGKILL) ? SIG_DFL : proctable[target_index].sig[signal];
-
- if (f == SIG_IGN)
- return 0;
- else if (f == SIG_DFL) {
- switch (signal) {
- case SIGKILL:
- case SIGINT:
- case SIGABRT:
- case SIGILL:
- case SIGFPE:
- case SIGSEGV:
- case SIGHUP:
- case SIGQUIT:
- case SIGTRAP:
- case SIGBUS:
- case SIGIOT:
- case SIGPIPE:
- close_all_internal(target_index);
- become_zombie(target_index,signal);
- if (target_index == current_process_index)
- start_some_process();
- else
- return 0;
- default:
- return 0;
- }
- }
- else {
- queue_up_signal_function(target_index,f,lomem);
- return 0;
- }
- }
- }
-
- long
- sleep_internal(long n)
- {
- int expire = TickCount() + n*60;
- while (TickCount() < expire)
- run_another_process();
- return 0;
- }
-
- int
- raise_internal(int signal,struct savearea_lomem *lomem)
- {
- return kill_internal(proctable[current_process_index].pid,signal,lomem);
- }
-
- int (*signal_internal(int sig,int (*f)()))()
- {
- int (*oldf)();
- if (sig <= 0 || sig > NSIG) return (int (*)())-1;
- oldf = proctable[current_process_index].sig[sig];
- proctable[current_process_index].sig[sig] = f;
- return oldf;
- }
-
- void
- _exit_internal(long status)
- {
- //printnum("In _exit",status);
- close_all_internal(current_process_index);
- become_zombie(current_process_index,status << 8);
- //printnum("Starting some other process in _exit_internal",0);
- start_some_process();
- }
-
- void
- exit_internal(long status)
- {
- int i;
-
- //printnum("In exit",status);
-
- for (i = 0; i<_NFILE; ++i)
- if (proctable[current_process_index].iob[i].flag != 0)
- fclose_internal(&proctable[current_process_index].iob[i],0L);
- _exit_internal(status);
- }
-
- long
- wait3_internal(long *status,long nohang,long ignored)
- {
- int i,pid;
-
- while (1) {
- /* Look for zombies */
- for (i = 0; i<MAXPROC; ++i) {
- if (proctable[i].pid != 0 &&
- proctable[i].ppid == proctable[current_process_index].pid &&
- proctable[i].zombie) {
- if (status != 0L) *status = proctable[i].status;
- pid = proctable[i].pid;
- remove_zombie(i);
- return pid;
- }
- }
-
- /* No zombies, look for children */
- for (i = 0; i<MAXPROC; ++i)
- if (proctable[i].ppid == proctable[current_process_index].pid)
- break;
-
- if (i == MAXPROC) {
- /* No children */
- set_errno(0);
- return -1;
- }
-
- /* Have children */
- if (nohang) return 0;
-
- /* Wait for a zombie */
- run_another_process();
- }
- }
-
- /*
- * vfork is done like this, from vfork.h:
- *
- * #define vfork() vfork2(setjmp(vfork1()))
- *
- * vfork1() returns the address of the save area of the current thread.
- * setjmp stores the current register state into that savearea, and
- * returns zero. vfork2 sees that zero, then stores the low-memory state
- * into that savearea, and simply changes the index of the current
- * process to that of the child process. Then vfork2 returns with a
- * result of 0, indicating to the caller that we are now in the child
- * thread. The parent thread is blocked until the child calls execvp.
- *
- * When the child calls execvp, the parent is freed, and the parent
- * continues execution by restoring the low-memory state and doing a
- * longjmp using the register state saved earlier. vfork2 sees this time
- * a non-zero input, and simply returns that input immediately. The
- * parent then executes normally.
- */
-
- int
- vfork1_internal(void)
- {
- proctable[current_process_index].savearea.regs.from_vfork = 1;
- return (int)&proctable[current_process_index].savearea.regs.jbuf;
- }
-
- int
- vfork2_internal(struct savearea_lomem *lomem,int setjmp_result)
- {
- int i,child_index;
- struct proc *child,*parent;
- char *child_cwd_string;
-
- if (setjmp_result != 0) return setjmp_result;
-
- child_index = get_free_proc();
- if (child_index == -1) { set_errno(EUNDOC); return -1; }
- child = &proctable[child_index];
- parent = &proctable[current_process_index];
-
- child_cwd_string = NewPtr(strlen(parent->cwd_string) + 1);
- strcpy(child_cwd_string,parent->cwd_string);
- initialize_one_proc(child,parent->pid,child_cwd_string,
- parent->environ_address,parent->errno_address);
- for (i = 0; i<_NFILE; ++i)
- child->iob[i] = parent->iob[i];
-
- for (i = 0; i<MAXDESC; ++i) {
- if (parent->fd[i].flavor != fd_flavor_unused) {
- add_given_fd(child_index,i,parent->fd[i].flavor,
- parent->fd[i].dispatch,parent->fd[i].object);
- ++(**parent->fd[i].object).count;
- }
- }
-
- parent->savearea.lomem = *lomem;
- parent->vforked = child->pid;
- parent->savearea.regs.d0_or_r3 = child->pid;
-
- current_process_index = child_index;
-
- return 0;
- }
-
- static char **child_argv;
- static int argc;
- static int (*pcode)();
-
- #if defined(powerc)
-
- static void
- execvp_bridge_from_601(void)
- {
- exit_internal(CallUniversalProc(bridge_68k_upp,bridge_68k_ProcInfo,
- pcode,argc,child_argv));
- }
-
- #else
-
- static void
- execvp_bridge_from_68k(void)
- {
- /* Call main of the extension */
- exit_internal((*pcode)(argc,child_argv));
- }
-
- #endif
-
- int
- execvp_internal(char *file,char **parent_argv)
- {
- FSSpec spec;
- short homeResFile;
- int (**hcode)();
- char **hz,*hp,*stacktop;
- int i,heap_size,parent_index,stack_size,exitcode;
- short err;
- jmp_buf bridge_jmp_buf;
- struct proc *child = &proctable[current_process_index],*parent;
- #if defined(powerc)
- #pragma options align=mac68k
- #endif
- struct size {
- short flags;
- long size;
- long minsize;
- } **hsize,**ssize;
- #if defined(powerc)
- #pragma options align=reset
- #endif
-
- hcode = 0L;
- hz = 0L;
-
- parent_index = pid_to_index(child->ppid);
- if (parent_index == -1) goto error;
- parent = &proctable[parent_index];
-
- /* Get resources of child. */
- err = unixfn2FSSpec_internal(file,&spec,0);
- if (err) goto error;
- child->homeResFile = FSpOpenResFile(&spec,fsRdPerm);
- if (child->homeResFile == -1) goto error;
-
- /* Get main code segment of child. */
- hcode = (int (**)())GetResource('CODE',0);
- if (hcode == 0L) goto error;
- /* hcode should be locked when loaded, but let's lock it anyway. */
- HLock((Handle)hcode);
-
- setup_stdio(current_process_index);
-
- /* Set up stack for child. The stack comes from within the heap of Emacs. */
- ssize = (struct size **)GetResource('SIZE',129);
- stack_size = (ssize == 0L ? LMGetDefltStack() : (**ssize).size);
- child->stackbottom = NewPtr(stack_size);
- if (MemError()) goto error;
- stacktop = child->stackbottom + stack_size;
-
- /* Set up heap for child. This will change the current heap. The heap
- comes from temporary memory. */
- hsize = (struct size **)GetResource('SIZE',128);
- heap_size = (hsize == 0L ? 32768 : (**hsize).size);
- hz = TempNewHandle(heap_size,&err);
- if (hz == 0L) goto error;
- child->heapHandle = hz;
- HLock(hz);
- hp = StripAddress(*hz);
- InitZone(0L,16,(char *)hp+heap_size,hp);
- if (MemError()) goto error;
-
- pcode = *hcode;
-
- /* Copy argument list into the heap of the child. */
- for (argc = 0; parent_argv[argc] != 0L; ++argc)
- ;
- child_argv = (char **)NewPtr((argc + 1) * sizeof(char *));
- if (MemError()) goto error;
- for (i = 0; i<argc; ++i) {
- child_argv[i] = NewPtr(strlen(parent_argv[i]) + 1);
- if (MemError()) goto error;
- strcpy(child_argv[i],parent_argv[i]);
- }
- child_argv[argc] = 0L;
-
- /* Give child its own errno. */
- child->errno_address = 0L;
-
- /* Parent can run now. */
- parent->vforked = 0;
-
- /* Start up the child. */
- LMHiHeapMark = child->stackbottom;
- LMSetHeapEnd(child->stackbottom);
- LMSetApplLimit(child->stackbottom);
-
- /* Code resources address off of a4, so we must set that. Each code resource
- gets its own stack, so we set that. The bridge code will push the parameters
- to main onto the stack, and deal with normal returns from main. */
- #if defined(THINK_C)
- bridge_jmp_buf[jmp_buf_pc] = (long)execvp_bridge_from_68k;
- bridge_jmp_buf[jmp_buf_a4] = (long)pcode;
- bridge_jmp_buf[jmp_buf_sp] = (long)stacktop;
- #elif defined(powerc)
- bridge_jmp_buf[jmp_buf_pc] = ((long **)execvp_bridge_from_601)[0];
- bridge_jmp_buf[jmp_buf_toc] = ((long **)execvp_bridge_from_601)[1];
- /* Leave stack space for three parameters plus linkage area. */
- stacktop -= 3 * sizeof(long) + sizeof(struct linkage_area);
- bridge_jmp_buf[jmp_buf_sp] = (long *)stacktop;
- #endif
- longjmp(bridge_jmp_buf,1);
-
- error:
- set_errno(EUNDOC);
- /* The caller must call _exit. */
- return -1;
- }
-
- static int max(a,b) { return a<b ? b : a; }
-
- int
- alarm_internal(int new_alarm_time)
- {
- int old_alarm_time;
- int now = TickCount();
-
- old_alarm_time = proctable[current_process_index].alarm_time;
- if (old_alarm_time != 0) old_alarm_time = max(0,(old_alarm_time - now) / 60);
- if (new_alarm_time != 0) new_alarm_time = new_alarm_time * 60 + now;
- proctable[current_process_index].alarm_time = new_alarm_time;
- return old_alarm_time;
- }
-
- long
- pause_internal(void)
- {
- run_another_process();
- return 0;
- }
-